#define _CRT_SECURE_NO_WARNINGS 1

#include "Mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>

#define FILE_MODE 0666


//两步：1.判断flag的值（w/a/r）（w+/a+/r+不考虑）根据flag的值，来确定open函数的打开方式并创建一个文件。
//     2.malloc一个FILE，并将FILE里的值处理好（初始化FILE）
_FILE* _fopen(const char* filename, const char* flag)           
{
    assert(filename);
    assert(flag);

    int f = 0;
    int fd = -1;

    //创建文件
    if (strcmp(flag, "w") == 0) 
    {
        f = (O_CREAT | O_WRONLY | O_TRUNC);
        fd = open(filename, f, FILE_MODE);
    }
    else if (strcmp(flag, "a") == 0)
    {
        f = (O_CREAT | O_WRONLY | O_APPEND);
        fd = open(filename, f, FILE_MODE);
    }
    else if (strcmp(flag, "r") == 0)
    {
        f = O_RDONLY;
        fd = open(filename, f);
    }
    else
        return NULL;               //flag的值都是错的，直接返回。

    if (fd == -1) return NULL;     //flag输对了，但open函数出错，返回


    //malloc一个FILE
    _FILE* fp = (_FILE*)malloc(sizeof(_FILE));
    if (fp == NULL) return NULL;


    //初始化FILE
    fp->fileno = fd;
    //fp->flag = FLUSH_LINE;
    fp->flag = FLUSH_ALL;       //缓冲区刷新方式
    fp->out_pos = 0;            //缓冲区内的字符个数 、光标指向位置的下标
}


//注意：这里的s字符串仅值以下类似的形式：
//     1."abc d"
//     2."abc d\n"
//     3."abc"  "abc\n"
//     不可以是 "abc d\nefg\n" 即不可含多个\n ---------此种情况可改进  
//思路：先将写入的信息全部拷贝进缓冲区，然后根据刷新方式，判断要不要刷新缓冲区，并写入文件（注意：要做好缓冲区的内容的个数管理）
int _fwrite(_FILE* fp, const char* s, int len)
{
    //将要写入的信息s全部拷贝进缓冲区outbuffer中，拷贝len个字符
    memcpy(&fp->outbuffer[fp->out_pos], s, len);            //不考虑memcpy函数失败，或outbuffer空间不够，s的后面一部分拷贝不进outbuffer中
    fp->out_pos += len;                                     //缓冲区的内容的个数管理


    //到这个地方，flag只能是001/010/100，必须与&后面内容相同，结果才能为1,不然为0
    if (fp->flag & FLUSH_NOW)    //无缓冲：直接刷新，将缓冲区内容写进文件    
    {
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
    else if (fp->flag & FLUSH_LINE)   //行刷新
    {
        if (fp->outbuffer[fp->out_pos - 1] == '\n') {          //"abc\n"
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }
    else if (fp->flag & FLUSH_ALL)
    {
        if (fp->out_pos == SIZE) {
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }

    return len;
}

//直接刷新缓冲区
void _fflush(_FILE* fp)
{
    if (fp->out_pos > 0) {
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
}

//思路：刷新缓冲区，关闭文件，释放FILE
void _fclose(_FILE* fp)
{
    if (fp == NULL) return;
    _fflush(fp);         //模拟程序退出时，要刷新缓冲区
    close(fp->fileno);
    free(fp);
}
